class Path < Pan::Component
  def initialize
    @stack = []
  end

  def current
    @stack.last
  end

  def push_with_name(obj, name)
    @stack.push([name, obj])
  end

  def choose(name)
    pos = @stack.pos{|nam,obj| nam == name}
    @stack = @stack[0..pos]
  end

  def render_on(html)
    html.div('class'=>'path') {
      @stack[0..-2].each do |name, obj|
        html.link(name) {
          choose(name)
        }
        html.lit(" >> ")
      end
      html.b {
        html.lit(@stack.last.first)
      }
    }
  end
end

class ObjectInspector < Pan::Component
  attr_reader :target
  def initialize()
    @path = Path.new
  end

  def target
    @path.current.second
  end

  def inspect(name, obj)
    @path.push_with_name(obj, name)
  end

  def target_values
    if target.class == Array
      res = []
      target.each_with_index{|ea,i| res << [i, ea]}
      res
    else
      target.instance_variables.map{|ea| [ea,target.instance_variable_get(ea)]}
    end
  end

  def render_on(html)
    html.render(@path)
    html.div(:class=>'object-inspector') {
      html.heading(target.class.to_s,2)
      html.lit(target.to_s.html_safe)
      html.table(:border=>1) {
        target_values.each {|key, val|
          html.tr {
            str = val.to_s.html_safe
            html.td {
              html.lit("#{key.to_s}")
            }
            html.td {
              # the below should really be done in a more OO way
              if val.class == Array
                render_array_on(val, html)
              elsif val.class == Hash
                render_hash_on(val, html)
              else
                render_item_on(key, val, html)
              end
            }
          }
        }
      }
    }
  end

  def render_array_on(arr, html)
    html.lit("[")
    arr.each_with_index do |ea,i|
      render_item_on(i, ea, html)
      html.lit(", ") if i < arr.size-1
    end
    html.lit("]")
  end

  def render_hash_on(val, html)
    html.lit("{")
    val.each do |key,val|
      render_item_on(key, key, html)
      html.lit("=>")
      render_item_on(key, val, html)
      html.lit(", ")
    end
    html.lit("}")
  end

  def render_item_on(key, obj, html)
    if obj.atom?
      html.lit("#{obj.to_s.html_safe}")
    else
      html.link("#{obj.class.name.to_s.html_safe}") {
        inspect(key.to_s.html_safe, obj)
      }
    end
  end

end
